package com.zhangdi.dwperms.filter;

import com.zhangdi.dwcommons.response.ResponseUtil;
import com.zhangdi.dwperms.commons.Contents;
import com.zhangdi.dwperms.commons.utils.SpringUtils;
import com.zhangdi.dwperms.token.AuthToken;
import com.zhangdi.dwperms.token.MSToken;
import com.zhangdi.dwperms.token.TokenUtils;
import com.zhangdi.dwtools.net.IPUtils;
import com.zhangdi.dwtools.text.StringUtils;
import com.zhangdi.dwtools.net.WebHelper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 认证过滤器
 */
public class RestFormAuthenticationFilter extends FormAuthenticationFilter {
    private static final Logger log = LoggerFactory
            .getLogger(RestFormAuthenticationFilter.class);

    TokenUtils tokenUtils;
    TokenUtils getTokenUtils() {
        if (null == tokenUtils) {
            tokenUtils = SpringUtils.getBean("tokenUtils");
        }
        return tokenUtils;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginRequest(request, response)) {
            return true;
        }

        boolean allowed = false;
        try {
            allowed = this.executeLogin(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return allowed || super.isPermissive(mappedValue);
    }

    @Override
    protected boolean pathsMatch(String path, ServletRequest request) {
        String requestURI = this.getPathWithinApplication(request);

        String[] strings = path.split("==");

        if (strings.length <= 1) {
            // 普通的 URL, 正常处理
            return this.pathsMatch(strings[0], requestURI);
        } else {
            // 获取当前请求的 http method.
            String httpMethod = WebUtils.toHttp(request).getMethod().toUpperCase();
            // 匹配当前请求的 url 和 http method 与过滤器链中的的是否一致
            return httpMethod.equals(strings[1].toUpperCase()) && this.pathsMatch(strings[0], requestURI);
        }
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthToken token = createToken(request, response);
        if (token == null) {
            return false;
        }
        try {
            Subject subject = this.getSubject(request, response);
            subject.login(token);
            return this.onSuccess((String) token.getCredentials(), response);
        } catch (AuthenticationException var5) {
            return this.onLoginFailure(token, var5, request, response);
        }
    }

    @Override
    protected AuthToken createToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authentication = httpServletRequest.getHeader(Contents.HEAD_KEY_TOKEN);
        if (StringUtils.isBlank(authentication)) {
            return null;
        }

        return getTokenUtils().decrypt(authentication);
    }

    protected boolean onSuccess(String newAccessToken, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setHeader(Contents.HEAD_KEY_TOKEN, newAccessToken);
        return true;
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        return false;
    }

    /**
     * 当没有权限被拦截时:
     * 如果是 AJAX 请求, 则返回 JSON 数据.
     * 如果是普通请求, 则跳转到配置 UnauthorizedUrl 页面.
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request,
                                     ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                //allow them to see the login page ;)
                return true;
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }

            if (WebHelper.isAjaxRequest(WebUtils.toHttp(request))) {
                if (log.isDebugEnabled()) {
                    log.debug("sessionId: [{}], ip: [{}] 请求 restful url : {}, 未登录被拦截.", httpServletRequest.getRequestedSessionId(), IPUtils.getIpAddr(),
                            this.getPathWithinApplication(request));
                }

                WebHelper.writeJson(ResponseUtil.error("未登录"), response);
            } else {
                saveRequestAndRedirectToLogin(request, response);
            }
            return false;
        }
    }
}